
#include "renderarea.h"
  QImage tex("textures/sk8.jpg");


RenderArea::RenderArea(QWidget *parent) : QWidget(parent)
{
   m_antialiased = false;
   m_transformed = false;
   m_image = 0;
   m_v = new RendererVertex[3];
   
   m_fpsTimer.start();
   m_frameCounter = 0;
   
   m_cube.load( "kocka.obj" );
//    m_cube.print();
   m_teapot.load( "teapot.obj" );
//    m_teapot.print();
   m_car.load( "ferarri.obj" );
//    m_car.print();
   m_model.load( "k.obj" );
   
   m_ambientLight.setPosition( gtypes::Vector3( 1, 1, 1 ) );
   m_light1.setPosition( gtypes::Vector3( 2, 1, 2 ) );
   m_light2.setPosition( gtypes::Vector3( -2, 1, 2 ) );
   m_light3.setPosition( gtypes::Vector3( 3, 0, 0 ) );
   
   m_frameTime.start();

   setBackgroundRole(QPalette::Base);
   setAutoFillBackground(true);
   
   tex=tex.rgbSwapped();
   m_renderer = &m_lineRenderer;
   m_texturedNearestRenderer.setTexture(&tex);
   m_texturedLinearRenderer.setTexture(&tex);
   m_perspectiveNearestRenderer.setTexture(&tex);
   m_perspectiveLinearRenderer.setTexture(&tex);
   m_demo = 0; // line renderer by default
   
   m_camera.setRenderer( m_renderer );
   
   setFocusPolicy( Qt::ClickFocus );
   
   connect( this, SIGNAL(shouldUpdate()), this, SLOT(update()) );
   
   connect( this, SIGNAL(setHiddenSurfaceRemovalEnabled(bool)), &m_cube, SLOT(setHiddenSurfaceRemovalEnabled(bool)) );
   connect( this, SIGNAL(setHiddenSurfaceRemovalEnabled(bool)), &m_car, SLOT(setHiddenSurfaceRemovalEnabled(bool)) );
   connect( this, SIGNAL(setHiddenSurfaceRemovalEnabled(bool)), &m_teapot, SLOT(setHiddenSurfaceRemovalEnabled(bool)) );
}

QSize RenderArea::minimumSizeHint() const
{
   return QSize(100, 100);
}

QSize RenderArea::sizeHint() const
{
   return QSize(400, 200);
}

void RenderArea::setPen(const QPen &pen)
{
   this->m_pen = pen;
   update();
}

void RenderArea::setBrush(const QBrush &brush)
{
   this->m_brush = brush;
   update();
}

void RenderArea::setAntialiased(bool antialiased)
{
   this->m_antialiased = antialiased;
   update();
}

void RenderArea::setTransformed(bool transformed)
{
   this->m_transformed = transformed;
   update();
}



void RenderArea::setRenderer(int renderer)
{
   switch( renderer )
   {
      default:
      case 0:
	 m_renderer = &m_lineRenderer;
	 break;
	 
      case 1:
	 m_renderer = &m_fillRenderer;
	 break;
	 
      case 2:
	 m_renderer = &m_coloredRenderer;
	 break;
	 
      case 3:
	 m_renderer = &m_texturedNearestRenderer;
	 break;
	 
      case 4:
	 m_renderer = &m_texturedLinearRenderer;
	 break;
	 
      case 5:
	 m_renderer = &m_perspectiveLinearRenderer;
	 break;
	 
      case 6:
	 m_renderer = &m_perspectiveNearestRenderer;
	 break;
   }
   
   m_camera.setRenderer( m_renderer );
}

void RenderArea::setDemo(int demo)
{
   m_demo = demo;
}


void RenderArea::setLightEnabled(bool enable)
{
   if( !enable )
   {
      disableAmbientLight();
      disableLight1();
      disableLight2();
      disableLight3();
   }
   else
   {
      setAmbientLightEnabled( m_ambientLightEnabled );
      setLight1Enabled( m_light1Enabled );
      setLight2Enabled( m_light2Enabled );
      setLight3Enabled( m_light3Enabled );
   }
}

void RenderArea::setAmbientLightEnabled(bool enable)
{
   m_ambientLightEnabled = enable;
   if( !enable )
   {
      m_lineRenderer.setAmbientLight( 0, 0, 0 );
      m_fillRenderer.setAmbientLight( 0, 0, 0 );
      m_coloredRenderer.setAmbientLight( 0, 0, 0 );
      m_texturedNearestRenderer.setAmbientLight( 0, 0, 0 );
      m_texturedLinearRenderer.setAmbientLight( 0, 0, 0 );
      m_perspectiveLinearRenderer.setAmbientLight( 0, 0, 0 );
      m_perspectiveNearestRenderer.setAmbientLight( 0, 0, 0 );
   }
   else
   {
      m_lineRenderer.setAmbientLight( m_ambientLight.m_r, m_ambientLight.m_g, m_ambientLight.m_b );
      m_fillRenderer.setAmbientLight( m_ambientLight.m_r, m_ambientLight.m_g, m_ambientLight.m_b );
      m_coloredRenderer.setAmbientLight( m_ambientLight.m_r, m_ambientLight.m_g, m_ambientLight.m_b );
      m_texturedNearestRenderer.setAmbientLight( m_ambientLight.m_r, m_ambientLight.m_g, m_ambientLight.m_b );
      m_texturedLinearRenderer.setAmbientLight( m_ambientLight.m_r, m_ambientLight.m_g, m_ambientLight.m_b );
      m_perspectiveLinearRenderer.setAmbientLight( m_ambientLight.m_r, m_ambientLight.m_g, m_ambientLight.m_b );
      m_perspectiveNearestRenderer.setAmbientLight( m_ambientLight.m_r, m_ambientLight.m_g, m_ambientLight.m_b );
   }
}


void RenderArea::setLight1Enabled(bool enable)
{
   m_light1Enabled = enable;
   
   // prevent readding
   m_lineRenderer.removeLight( &m_light1 );
   m_fillRenderer.removeLight( &m_light1 );
   m_coloredRenderer.removeLight( &m_light1 );
   m_texturedNearestRenderer.removeLight( &m_light1 );
   m_texturedLinearRenderer.removeLight( &m_light1 );
   m_perspectiveLinearRenderer.removeLight( &m_light1 );
   m_perspectiveNearestRenderer.removeLight( &m_light1 );
   
   // add if enabled (removed already so don't have to reremove it)
   if( enable )
   {
      m_lineRenderer.addLight( &m_light1 );
      m_fillRenderer.addLight( &m_light1 );
      m_coloredRenderer.addLight( &m_light1 );
      m_texturedNearestRenderer.addLight( &m_light1 );
      m_texturedLinearRenderer.addLight( &m_light1 );
      m_perspectiveLinearRenderer.addLight( &m_light1 );
      m_perspectiveNearestRenderer.addLight( &m_light1 );
   }
}

void RenderArea::setLight2Enabled(bool enable)
{
   m_light2Enabled = enable;
   
   // prevent readding
   m_lineRenderer.removeLight( &m_light2 );
   m_fillRenderer.removeLight( &m_light2 );
   m_coloredRenderer.removeLight( &m_light2 );
   m_texturedNearestRenderer.removeLight( &m_light2 );
   m_texturedLinearRenderer.removeLight( &m_light2 );
   m_perspectiveLinearRenderer.removeLight( &m_light2 );
   m_perspectiveNearestRenderer.removeLight( &m_light2 );
   
   // add if enabled (removed already so don't have to reremove it)
   if( enable )
   {
      m_lineRenderer.addLight( &m_light2 );
      m_fillRenderer.addLight( &m_light2 );
      m_coloredRenderer.addLight( &m_light2 );
      m_texturedNearestRenderer.addLight( &m_light2 );
      m_texturedLinearRenderer.addLight( &m_light2 );
      m_perspectiveLinearRenderer.addLight( &m_light2 );
      m_perspectiveNearestRenderer.addLight( &m_light2 );
   }
}

void RenderArea::setLight3Enabled(bool enable)
{
   m_light3Enabled = enable;
   
      // prevent readding
   m_lineRenderer.removeLight( &m_light3 );
   m_fillRenderer.removeLight( &m_light3 );
   m_coloredRenderer.removeLight( &m_light3 );
   m_texturedNearestRenderer.removeLight( &m_light3 );
   m_texturedLinearRenderer.removeLight( &m_light3 );
   m_perspectiveLinearRenderer.removeLight( &m_light3 );
   m_perspectiveNearestRenderer.removeLight( &m_light3 );
   
   // add if enabled (removed already so don't have to reremove it)
   if( enable )
   {
      m_lineRenderer.addLight( &m_light3 );
      m_fillRenderer.addLight( &m_light3 );
      m_coloredRenderer.addLight( &m_light3 );
      m_texturedNearestRenderer.addLight( &m_light3 );
      m_texturedLinearRenderer.addLight( &m_light3 );
      m_perspectiveLinearRenderer.addLight( &m_light3 );
      m_perspectiveNearestRenderer.addLight( &m_light3 );
   }
}

void RenderArea::disableAmbientLight()
{
   m_lineRenderer.setAmbientLight( 0, 0, 0 );
   m_fillRenderer.setAmbientLight( 0, 0, 0 );
   m_coloredRenderer.setAmbientLight( 0, 0, 0 );
   m_texturedNearestRenderer.setAmbientLight( 0, 0, 0 );
   m_texturedLinearRenderer.setAmbientLight( 0, 0, 0 );
   m_perspectiveLinearRenderer.setAmbientLight( 0, 0, 0 );
   m_perspectiveNearestRenderer.setAmbientLight( 0, 0, 0 );
}

void RenderArea::disableLight1()
{
   m_lineRenderer.removeLight( &m_light1 );
   m_fillRenderer.removeLight( &m_light1 );
   m_coloredRenderer.removeLight( &m_light1 );
   m_texturedNearestRenderer.removeLight( &m_light1 );
   m_texturedLinearRenderer.removeLight( &m_light1 );
   m_perspectiveLinearRenderer.removeLight( &m_light1 );
   m_perspectiveNearestRenderer.removeLight( &m_light1 );
}

void RenderArea::disableLight2()
{
   m_lineRenderer.removeLight( &m_light2 );
   m_fillRenderer.removeLight( &m_light2 );
   m_coloredRenderer.removeLight( &m_light2 );
   m_texturedNearestRenderer.removeLight( &m_light2 );
   m_texturedLinearRenderer.removeLight( &m_light2 );
   m_perspectiveLinearRenderer.removeLight( &m_light2 );
   m_perspectiveNearestRenderer.removeLight( &m_light2 );
}

void RenderArea::disableLight3()
{
   m_lineRenderer.removeLight( &m_light3 );
   m_fillRenderer.removeLight( &m_light3 );
   m_coloredRenderer.removeLight( &m_light3 );
   m_texturedNearestRenderer.removeLight( &m_light3 );
   m_texturedLinearRenderer.removeLight( &m_light3 );
   m_perspectiveLinearRenderer.removeLight( &m_light3 );
   m_perspectiveNearestRenderer.removeLight( &m_light3 );
}


void RenderArea::setAmbientRed(int c)
{
   m_ambientLight.setRed( c );
}

void RenderArea::setAmbientGreen(int c)
{
   m_ambientLight.setGreen( c );
}

void RenderArea::setAmbientBlue(int c)
{
   m_ambientLight.setBlue( c );
}




void RenderArea::setLight1Red(int c)
{
   m_light1.setRed( c );
}

void RenderArea::setLight1Green(int c)
{
   m_light1.setGreen( c );
}

void RenderArea::setLight1Blue(int c)
{
   m_light1.setBlue( c );
}



void RenderArea::setLight2Red(int c)
{
   m_light2.setRed( c );
}

void RenderArea::setLight2Green(int c)
{
   m_light2.setGreen( c );
}

void RenderArea::setLight2Blue(int c)
{
   m_light2.setBlue( c );
}



void RenderArea::setLight3Red(int c)
{
   m_light3.setRed( c );
}

void RenderArea::setLight3Green(int c)
{
   m_light3.setGreen( c );
}

void RenderArea::setLight3Blue(int c)
{
   m_light3.setBlue( c );
}

void RenderArea::paintEvent(QPaintEvent *event )
{
   if( !m_image )
   {
      m_image = new QImage( size(), QImage::Format_RGB888 );
      m_lineRenderer.setTarget( m_image );
      m_fillRenderer.setTarget( m_image );
      m_coloredRenderer.setTarget( m_image );
      m_texturedNearestRenderer.setTarget( m_image );
      m_texturedLinearRenderer.setTarget( m_image );
      m_perspectiveLinearRenderer.setTarget( m_image );
      m_perspectiveNearestRenderer.setTarget( m_image );
   }
   m_image->fill(0);

   if( m_fpsTimer.elapsed() >= 1000 ) slotUpdateFps();
   
   int msecSinceLastFrame = m_frameTime.elapsed();
   m_frameTime.restart();
  static float a=0;
   a += msecSinceLastFrame/1000.f;
   switch( m_demo )
   {
      case 0: // rotating triangle



         m_v[0].pos.set(240,25,1);
         m_v[1].pos.set(240+sin(a)*200,300+cos(a)*50,1);
         m_v[2].pos.set(240-sin(a)*200,300-cos(a)*50,1);
         m_v[0].r=255; m_v[0].g=0; m_v[0].b=0;
         m_v[1].r=0; m_v[1].g=255; m_v[1].b=0;
         m_v[2].r=0; m_v[2].g=0; m_v[2].b=255;
         m_v[0].tex.x=0.5f; m_v[0].tex.y=0;
         m_v[1].tex.x=0; m_v[1].tex.y=1;
         m_v[2].tex.x=1; m_v[2].tex.y=1;


         m_renderer->render( m_v );
     //    m_lineRenderer.render(m_v);

	 break;
	 
	 
	 
	 
      case 1: // cube
          m_renderer->identity();

	  
          m_cube.moveTo(0,-0.2f,5.0f);
          m_cube.rotate(0,1,0,a*30);

	  m_renderer->setProjectionMatrix(60,m_renderer->aspect(),0.1,1000);

          if (m_renderer == &m_perspectiveLinearRenderer || m_renderer == &m_perspectiveNearestRenderer)
          {
              float res=10;
              Vertex3D vx;
              std::vector<Vertex3D> v;

              vx.r=vx.g=vx.b=255;
              vx.normal.set(0,0,-1);
              v.push_back(vx);v.push_back(vx);v.push_back(vx);

              RendererVertex rv[3];

              m_renderer->identity();
              m_renderer->translate(0,-0.2f,5.0f);
              m_renderer->rotate(0,1,0,a*30);
              int angle=((int) (a*30))%360;
              for (int iter=0;iter<4;iter++)
              {
                  if (iter == 0 && angle > 100 && angle < 260 ||
                      iter == 3 && angle > 190 && angle < 350 ||
                      iter == 2 && (angle > 280 || angle < 80) ||
                      iter == 1 && angle > 10 && angle < 10+160)
                  {
                      for (int y=0;y<res;y++)
                      {
                          for (int x=0;x<res;x++)
                          {
                              v[0].pos.set(x*2/res-1.0,1-y*2/res,1);
                              v[0].tex.set(x/res,1-y/res);
                              v[1].pos.set((x+1.1f)*2/res-1,1-y*2/res,1);
                              v[1].tex.set((x+1.1f)/res,1-y/res);
                              v[2].pos.set(x*2/res-1,1-(y+1.1f)*2/res,1);
                              v[2].tex.set(x/res,1-(y+1.1f)/res);
                              m_renderer->transform(v,rv);

                              m_renderer->render(rv);
                              v[0].pos.set((x+1.1f)*2/res-1,1-y*2/res,1);
                              v[0].tex.set((x+1.1f)/res,1-y/res);
                              v[1].pos.set((x+1.1f)*2/res-1,1-(y+1.1f)*2/res,1);
                              v[1].tex.set((x+1.1f)/res,1-(y+1.1f)/res);
                              v[2].pos.set(x*2/res-1,1-(y+1.1f)*2/res,1);
                              v[2].tex.set(x/res,1-(y+1.1f)/res);
                              m_renderer->transform(v,rv);
                              m_renderer->render(rv);
                          }
                       }
                    }
                 m_renderer->rotate(0,1,0,90);
             }

          }
          else m_cube.render( m_renderer );
          break;
	  
      case 2: // teapot
	 m_renderer->identity();

	 m_teapot.scale( 0.05 );
	 m_teapot.moveTo(0,-1.0f,0.7f);
	 m_teapot.rotate(0,1,0,a*30);
	 m_renderer->setProjectionMatrix(60,m_renderer->aspect(),0.1,1000);

	 m_teapot.render( m_renderer );
	 break;
	 
      case 3: // car
	 m_renderer->identity();

	 m_car.moveTo(0,-0.2f,0.7f);
	 m_car.rotate(0,1,0,a*30);
	 m_renderer->setProjectionMatrix(60,m_renderer->aspect(),0.1,1000);

	 m_car.render( m_renderer );
	 break;
	 
      case 4: // car fly through
	 m_renderer->identity();
	 m_car.moveTo(0,-0.2f,0.7f);
	 m_camera.update();
	 m_renderer->setProjectionMatrix(60,m_renderer->aspect(),0.1,1000);
	 m_car.render( m_renderer );
	 break;
	 
      default:
	 break;
   }
   
   
   
   QPainter painter( this );
   painter.drawImage( rect(), *m_image, m_image->rect() );
   
   m_frameCounter++;
   event->accept();
   emit shouldUpdate();
}


void RenderArea::resizeEvent(QResizeEvent* event)
{
   // delete m_image, paintEvent will create it with right size so no need to do it here
   delete m_image;
   m_image = 0;
   QWidget::resizeEvent(event);
}


void RenderArea::keyPressEvent(QKeyEvent* event)
{
   if( m_demo == 4 )
   {
      if( event->key() == Qt::Key_Up ) m_camera.moveForward( 0.01 );
      if( event->key() == Qt::Key_Down ) m_camera.moveForward( -0.01 );
      if( event->key() == Qt::Key_Left ) m_camera.strafeRight( 0.01 );
      if( event->key() == Qt::Key_Right ) m_camera.strafeRight( -0.01 );
   }
    QWidget::keyPressEvent(event);
}

void RenderArea::mousePressEvent(QMouseEvent* event)
{
   m_oldMouseX = event->x();
   m_oldMouseY = event->y();
   
    QWidget::mousePressEvent(event);
}


void RenderArea::mouseMoveEvent(QMouseEvent* event)
{
   if( m_demo == 4 )
   {
      int relativeX = m_oldMouseX - event->x();
      int relativeY = m_oldMouseY - event->y();
      m_oldMouseX = event->x();
      m_oldMouseY = event->y();
      m_camera.rotateY( (float)relativeX/2 );
      m_camera.rotateX( -(float)relativeY/2 );
   }
    QWidget::mouseMoveEvent(event);
}


void RenderArea::slotUpdateFps()
{
   int frames = m_frameCounter;
   m_frameCounter = 0;
   m_fpsTimer.restart();
   emit updateFps( frames );
}



RenderArea::~RenderArea()
{
   delete m_image;
   delete[] m_v;
}
